package com.py.custom.common.web.exception;

import com.py.custom.common.web.response.BaseResponse;
import com.py.custom.common.web.utils.ResponseUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.*;

import static com.py.custom.common.web.enums.ResponseEnums.*;

/**
 * description： 全局异常处理类
 *
 * @author pengyou
 * @version 1.0.0
 * @date 2021/10/17
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

    private static final String PARAM_TYPE = "arg";

    /**
     * 系统异常，未知错误
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(value = {Exception.class})
    public BaseResponse<Object> systemException(Exception ex) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        ex.printStackTrace(pw);
        // 堆栈信息，用来排查错误
        String stackStr = sw.toString();
        return ResponseUtils.fail(SYSTEM_ERROR, stackStr, null);
    }

    /**
     * 业务异常，通过 BizException 抛出的
     *
     * @param ex 自定义的业务异常
     * @return
     */
    @ExceptionHandler(value = {BizException.class})
    public BaseResponse<Object> bizException(BizException ex) {
        // 前端提示信息
        String message = ex.getMessage();
        return ResponseUtils.fail(BIZ_ERROR, message);
    }

    /**
     * 请求参数校验异常
     *
     * @param ex 有
     * @return
     */
    @ExceptionHandler(value = {MethodArgumentNotValidException.class})
    public BaseResponse<Object> paramException(MethodArgumentNotValidException ex) {
        // 前端提示信息
        List<Map<String, String>> errorList = new ArrayList<>();
        StringBuilder builder = new StringBuilder();
        BindingResult bindingResult = ex.getBindingResult();
        if (bindingResult.hasErrors()) {
            List<ObjectError> allErrors = bindingResult.getAllErrors();
            Map<String, String> errorData = new HashMap<>();
            for (ObjectError error : allErrors) {
                if (error instanceof FieldError) {
                    FieldError fieldError = (FieldError) error;
                    errorData.put("field", fieldError.getField());
                    errorData.put("message", fieldError.getDefaultMessage());
                } else {
                    errorData.put("field", error.getObjectName());
                    errorData.put("message", error.getDefaultMessage());
                }
                builder.append(error.getDefaultMessage()).append("\t");
                errorList.add(errorData);
            }
        }
        return ResponseUtils.fail(PARAM_ERROR, builder.toString(), errorList);
    }

    /**
     * service方法 返回值/参数 校验异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(value = {ConstraintViolationException.class})
    public BaseResponse<Object> returnViolationException(ConstraintViolationException ex) {
        Set<ConstraintViolation<?>> violations = ex.getConstraintViolations();
        StringBuilder errorStr = new StringBuilder();
        for (ConstraintViolation<?> violation : violations) {
            errorStr.append(violation.getMessage()).append("\n");
        }

        String message = ex.getMessage();
        if (message.contains(PARAM_TYPE)) {
            // 参数异常
            return ResponseUtils.fail(PARAM_ERROR, errorStr.toString());
        }
        return ResponseUtils.fail(RETURN_VIOLATION_ERROR, errorStr.toString());
    }

    /**
     * 空指针异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(value = {NullPointerException.class})
    public BaseResponse<Object> nullPointException(NullPointerException ex) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        ex.printStackTrace(pw);
        // 堆栈信息，用来排查错误
        String stackStr = sw.toString();
        return ResponseUtils.fail(NULL_POINT_ERROR, stackStr);
    }
}

